#include "tabbuttonspanel.h"
#include <QPushButton>
#include "../terminalglobals.h"
#include "tabbutton.h"
#include <QDataStream>
#include <QPainter>
#include "scrollbutton.h"

using namespace ExecTerminalImplNS;

const int SCROLL_BUTTON_HEIGHT = 15;
const int BUTTON_HEIGHT = 24;
const int BUTTONS_SPACING = 4;
const int VERTICAL_MARGIN = 12;
const int HORIZONTAL_MARGIN = 10;
const int MINIMUM_BUTTON_WIDTH = 10;
const QColor BACKGROUND_COLOR(249, 252, 253);
const QColor LINE_COLOR(227, 226, 226); 
const int TOP_WHITE_MARGIN = 1;

TabButtonsPanel::TabButtonsPanel(QWidget * parent): QWidget(parent),
theTopButton(0),
theBottomButton(0),
theFirstVisibleNumber(0),
theVisibleCount(0),
theCurrentNumber(0),
theButtonsHeight(BUTTON_HEIGHT),
theButtonsSpacing(BUTTONS_SPACING),
theMinimumVertMargin(VERTICAL_MARGIN),
theHorMargin(HORIZONTAL_MARGIN)
{
    theTopButton = new ScrollButton(this);
    theTopButton->resize(theTopButton->width(), SCROLL_BUTTON_HEIGHT);
    theTopButton->move(theHorMargin, theMinimumVertMargin);
    theTopButton->setFocusPolicy(Qt::NoFocus);
    connect(theTopButton, SIGNAL(clicked()), SLOT(moveUp()));

    theBottomButton = new ScrollButton(this);
    static_cast<ScrollButton*>(theBottomButton)->setOrientation(DOWN_ORIENTATION);
    theBottomButton->resize(theBottomButton->width(), SCROLL_BUTTON_HEIGHT);
    theBottomButton->move(theHorMargin, theMinimumVertMargin);
    theBottomButton->setFocusPolicy(Qt::NoFocus);
    connect(theBottomButton, SIGNAL(clicked()), SLOT(moveDown()));

    updateMinimumHeight();
    initLayoutButtons();
}

int TabButtonsPanel::buttonsHeight()const
{
    return theButtonsHeight;
}

void TabButtonsPanel::setButtonsHeight(int aHeight)
{
    checkedSetValue(&theButtonsHeight, aHeight);
}

void TabButtonsPanel::checkedSetValue(int * val, int newVal)
{
    if (newVal != *val)
    {
        *val = newVal;
        updateMinimumHeight();
        layoutButtons();
    }
}

int TabButtonsPanel::buttonsSpacing()const
{
    return theButtonsSpacing;   
}

void TabButtonsPanel::setButtonsSpacing(int aSpacing)
{
    checkedSetValue(&theButtonsSpacing, aSpacing);
}

int TabButtonsPanel::minimumVertMargin()const
{
    return theMinimumVertMargin;
}

void TabButtonsPanel::setMinimumVertMargin(int aMargin)
{
    checkedSetValue(&theMinimumVertMargin, aMargin);
}

int TabButtonsPanel::horMargin()const
{
    return theHorMargin;
}

void TabButtonsPanel::setHorMargin(int aMargin)
{
    if (theHorMargin != aMargin)
    {
        theHorMargin = aMargin;
        initLayoutButtons();
    }
}

void TabButtonsPanel::moveUp()
{
    if (theFirstVisibleNumber > 0)
    {
        --theFirstVisibleNumber;
        updateVisibleButtons();
        if (theFirstVisibleNumber <= 0)
        {
            theTopButton->setEnabled(false);
        }
        theBottomButton->setEnabled(true);
    }
}

void TabButtonsPanel::moveDown()
{
    if (theFirstVisibleNumber + theVisibleCount < theTabButtons.count())
    {
        ++theFirstVisibleNumber;
        updateVisibleButtons();
        if (theFirstVisibleNumber + theVisibleCount >= theTabButtons.count())
        {
            theBottomButton->setEnabled(false);
        }
        theTopButton->setEnabled(true);
    }
}

void TabButtonsPanel::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    const QPixmap & backPix = pixmapForPath(":Resources/left_bookmarks.png");
    painter.drawTiledPixmap(0, TOP_WHITE_MARGIN, width(), backPix.height(), backPix);
    const int fillRectTop = TOP_WHITE_MARGIN + backPix.height();
    painter.fillRect(QRect(0,  fillRectTop, width(), height() - fillRectTop), BACKGROUND_COLOR);
    painter.fillRect(QRect(0,  0, width(), TOP_WHITE_MARGIN), palette().base());
    painter.setPen(LINE_COLOR);
    painter.drawLine(0,  height() - 1, width(), height() - 1);
}

void TabButtonsPanel::resizeEvent(QResizeEvent *)
{
    layoutButtons();
}

void TabButtonsPanel::layoutButtons()
{
    setUpdatesEnabled(false);
    const int buttonsWidth = width() - 2 * theHorMargin;
    int myUsefulHeight = height() - 2 * theMinimumVertMargin;
    int visibleButtonsCount = qMin((myUsefulHeight + theButtonsSpacing ) / (theButtonsHeight + theButtonsSpacing),
        theTabButtons.count());
    if (visibleButtonsCount < theTabButtons.count())
    {
        theTopButton->show();
        theBottomButton->show();
        theTopButton->resize(buttonsWidth, theTopButton->height());
        theBottomButton->resize(buttonsWidth, theTopButton->height());
        theBottomButton->move(theHorMargin, height() - theMinimumVertMargin - theBottomButton->height());
        myUsefulHeight -= 2 * SCROLL_BUTTON_HEIGHT + 2 * theButtonsSpacing;
        visibleButtonsCount = (myUsefulHeight + theButtonsSpacing ) / (theButtonsHeight + theButtonsSpacing);
    }
    else
    {
        theFirstVisibleNumber = 0;
        theTopButton->hide();
        theBottomButton->hide();
    }
    if (theVisibleCount > 0)
    {
        theFirstVisibleNumber = findNewFirstVisiblePos(theFirstVisibleNumber,
            theVisibleCount, 
            visibleButtonsCount, 
            theCurrentNumber, 
            theTabButtons.count());
    }
    theVisibleCount = qMin(visibleButtonsCount, theTabButtons.count());
    updateScrollBottonsEnabledState();
    updateVisibleButtons();
    setUpdatesEnabled(true);
}

void TabButtonsPanel::initLayoutButtons()
{
    theTopButton->move(theHorMargin, theMinimumVertMargin);
    theBottomButton->move(theHorMargin, theBottomButton->y());
    layoutButtons();
}

void TabButtonsPanel::updateMinimumHeight()
{
    const int minHeight = 2 * theMinimumVertMargin + 2 * SCROLL_BUTTON_HEIGHT 
        + theButtonsHeight + 2 * theButtonsSpacing; 
    setMinimumHeight(minHeight);
    setMinimumWidth(2 * theHorMargin + MINIMUM_BUTTON_WIDTH);
}

void TabButtonsPanel::updateVisibleButtons()
{
    int initYPos = theMinimumVertMargin;
    if (theVisibleCount < theTabButtons.count())
    {
        int myUsefulHeight = height() - 2 * (theMinimumVertMargin + SCROLL_BUTTON_HEIGHT + theButtonsSpacing);
        initYPos += SCROLL_BUTTON_HEIGHT + theButtonsSpacing 
            + (myUsefulHeight - (theVisibleCount * (theButtonsHeight + theButtonsSpacing) - theButtonsSpacing)) / 2;
    }
    const int buttonsWidth = width() - 2 * theHorMargin;
    for (int i = 0; i < theTabButtons.count(); ++i)
    {
        if (i >= theFirstVisibleNumber && i < theFirstVisibleNumber + theVisibleCount)
        {
            theTabButtons[i]->show();
            theTabButtons[i]->move(theHorMargin, initYPos);
            initYPos += theTabButtons[i]->height() + theButtonsSpacing;
            theTabButtons[i]->resize(buttonsWidth, theTabButtons[i]->height());
        }
        else
        {
            theTabButtons[i]->hide();
        }
    }
}

void TabButtonsPanel::updateCurrentIndex(const QVariant & data)
{
    Q_ASSERT(qobject_cast<TabButton*>(sender()));
    TabButton * tab = static_cast<TabButton*>(sender());
    theCurrentNumber = theTabButtons.indexOf(tab);
    emit currentChanged(theCurrentNumber, data);
}

void TabButtonsPanel::removeDeletingTab(QVariant )
{
    Q_ASSERT(qobject_cast<TabButton*>(sender()));
    const int tabIndex = theTabButtons.indexOf(static_cast<TabButton*>(sender()));
    QVariant tabData = theTabButtons.at(tabIndex)->data();
    const bool wasChecked = theTabButtons.at(tabIndex)->isChecked();
    emit tabDeleting(tabData);

    theTabButtons.at(tabIndex)->deleteLater();
    theTabButtons.takeAt(tabIndex);
    if (tabIndex < theCurrentNumber)
    {
        --theCurrentNumber;
    }
    const int newCount = theTabButtons.count();
    if (newCount == 0)
    {
        theFirstVisibleNumber = -1;
        theVisibleCount = 0;
    }
    else if (wasChecked)
    {
        Q_ASSERT(theCurrentNumber >= 0);
        if (theCurrentNumber >= theTabButtons.count())
        {
            theCurrentNumber = theTabButtons.count() - 1;
        }
        theTabButtons.at(theCurrentNumber)->setChecked(true);
    }
    emit tabClosed(tabIndex, tabData);
    layoutButtons();
    updateVisibleButtons();
}

QByteArray TabButtonsPanel::saveState()const
{
    QByteArray state; 
    QDataStream out(&state, QIODevice::WriteOnly);
    out << static_cast<qint32>(theTabButtons.count());
    if (theTabButtons.count() > 0)
    {
        foreach (TabButton * tab, theTabButtons)
        {
            out << tab->data();
            out << tab->icon();
            out << tab->text();
        }
        out << static_cast<qint32>(theCurrentNumber);
    }
    return state;
}

void TabButtonsPanel::restoreState(const QByteArray & state)
{
    setUpdatesEnabled(false);
    QDataStream in(state);
    qint32 readVariable;
    in >> readVariable;
    blockSignals(true);
    while (theTabButtons.count() > readVariable)
    {
        delete theTabButtons.takeLast();
    }
    while (theTabButtons.count() < readVariable)
    {
        theTabButtons.append(new TabButton(this));
        theTabButtons.last()->resize(width() - 2 * theHorMargin, theButtonsHeight);
        connect(theTabButtons.last(), SIGNAL(selected(const QVariant &)), SLOT(updateCurrentIndex(const QVariant &)));
        connect(theTabButtons.last(), SIGNAL(deleting(QVariant)), SLOT(removeDeletingTab(QVariant)));    
    }
    if (readVariable > 0)
    {
        foreach (TabButton * tab, theTabButtons)
        {
            QVariant tData;
            in >> tData;
            tab->setData(tData);
            QIcon tIco;
            in >> tIco;
            tab->setIcon(tIco);
            QString tText; 
            in >> tText;
            tab->setText(tText);
        }
        in >> readVariable;
        theTabButtons[readVariable]->setChecked(true);
    }
    blockSignals(false);
    if (theFirstVisibleNumber > theTabButtons.count() || theFirstVisibleNumber + theVisibleCount > theTabButtons.count())
    {
        theFirstVisibleNumber = 0;
        theVisibleCount = theTabButtons.count();
    }
    layoutButtons();
    setUpdatesEnabled(true);
}   
    
void TabButtonsPanel::updateScrollBottonsEnabledState()
{
    theTopButton->setEnabled(theFirstVisibleNumber > 0);
    theBottomButton->setEnabled(theFirstVisibleNumber + theVisibleCount < theTabButtons.count());
}
    
